﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Forms;

using Microsoft.Coding4Fun.TweetCraft.Tray.Support;
using Microsoft.Coding4Fun.TweetCraft.Twitter;
using Microsoft.Coding4Fun.TweetCraft.Wow;

using Microsoft.SqlServer.MessageBox;

using FontStyle = System.Drawing.FontStyle;
using IWin32Window = System.Windows.Forms.IWin32Window;

namespace Microsoft.Coding4Fun.TweetCraft.Tray
{
    /// <summary>
    /// The TweetCraft Notification Tray icon front-end application.
    /// </summary>
    public sealed partial class TweetCraftApplication : IDisposable
    {
		//The twitter site url
        private const string TwitterUrl = "http://twitter.com";

		// Name of the update file for TweetCraft Addon
		private const string TweetCraftAddOnUpdateFileName = "TweetCraftAddOn.zip";

        // Timeout for all the notification balloons we show
        private const int BalloonTipTimeout = 5000;

        // The '/silent' command-line argument is used when TweetCraft is run when Windows starts up
        private const string SilentArgument = "/silent";

        // Name of the addon to look for in World of Warcraft
        private const string TweetCraftAddOnName = "TweetCraft";

        // A command name used when popping up a balloon that settings are incomplete
        private const string ShowTweetCraftSettingsCommand = "ShowTweetCraftSettings";

        // Contains the list of host names that TweetCraft will launch using Process.Start
        private static readonly string[] UrlWhitelist = new[]
                                               {
                                                   "twitter.com",
                                                   "twitpic.com"
                                               };

        private MainNativeWindow mainNativeWindow;
        private SettingsWindow settingsWindow;
        private ITweetCraft tweetCraft;
        private bool showStartupNotification;

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Application.Startup"/> event and adds the tray
        /// icon to the notification area.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.StartupEventArgs"/> that contains the event data.</param>
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            Trace.TraceInformation("TweetCraft starting up...");

            // Enable WinForms visual styles to let our exception message boxes shine
            System.Windows.Forms.Application.EnableVisualStyles();

            // Initialize logging, error handling and the icon in the notification area
            InitializeLogging();
            InitializeErrorHandling();
            InitializeNotifyIcon();
            InitializeNativeWindow();

            // Perform the start-up checks
            if (PerformStartupChecks())
            {
                // Process the command-line arguments ('/silent' switch to suppress startup notification bubble)
                ProcessArguments(e.Args);

                // Initialize TweetCraft
                InitializeTweetCraft();
            }
            else
            {
                // Startup checks failed, shutdown
                Shutdown();
            }
        }

        private void InitializeNativeWindow()
        {
            // Initialize native window
            mainNativeWindow = new MainNativeWindow(this);

            // Initialize create parameters
            var createParams = new CreateParams
                               {
                                   Caption = "TweetCraft HiddenNativeWindow"
                               };

            // Create a native handle that will received window messages
            mainNativeWindow.CreateHandle(createParams);
        }

        private void ProcessArguments(IEnumerable<string> args)
        {
            // Only show the startup notification if the arguments do not contain '/silent'
            showStartupNotification = !args.Contains(SilentArgument);
        }

        private bool PerformStartupChecks()
        {
			using (TraceScope trace = TraceScope.Information("Performing startup checks..."))
			{
				Trace.TraceInformation("Checking for running instances of TweetCraft...");

				// Check whether the TweetCraft tray application is already running
				if (Process.GetCurrentProcess().IsAlreadyRunning())
				{
					Trace.TraceError("TweetCraft is already running.");

					ShowMessageBox(Strings.TweetCraftAlreadyRunning, Strings.TweetCraftTitle, ExceptionMessageBoxButtons.OK, ExceptionMessageBoxSymbol.Warning, true);
					return false;
				}

				Trace.TraceInformation("Checking World of Warcraft installation...");

				// Initialize a new instance of the World of Warcraft class
				var worldOfWarcraft = new WorldOfWarcraft();

				// Check whether World of Warcraft is detected
				if (!worldOfWarcraft.IsDetected)
				{
					Trace.TraceError("World of Warcraft was not detected.");

					ShowMessageBox(Strings.WorldOfWarcraftNotDetected, Strings.TweetCraftTitle, ExceptionMessageBoxButtons.OK, ExceptionMessageBoxSymbol.Error, true);
					return false;
				}

				// Check whether World of Warcraft accounts are available
				if (!worldOfWarcraft.AreAccountsAvailable)
				{
					Trace.TraceError("World of Warcraft has no accounts available.");

					ShowMessageBox(Strings.NoAccountsAvailable, Strings.TweetCraftTitle, ExceptionMessageBoxButtons.OK, ExceptionMessageBoxSymbol.Error, true);
					return false;
				}

                //Check for newer version of TweetCraft is available
                PerformTweetCraftUpdateCheck();

				//Check for newer version of TweetCraft WoW AddOn is available
				bool checkResult = PerformTweetCraftAddonCheck(worldOfWarcraft);

                trace.Complete(checkResult ? "Startup checks passed." : "Startup checks failed.");
				return checkResult;
			}
        }

		/// <summary>
		/// Performs the TweetCraft addon check.
		/// </summary>
		/// <returns></returns>
	private bool PerformTweetCraftAddonCheck(IWorldOfWarcraft worldOfWarcraft)
		{
			Trace.TraceInformation("Checking TweetCraft add-on...");

			bool addOnInstalled = worldOfWarcraft.IsAddOnInstalled(TweetCraftAddOnName);

			// Check whether TweetCraft addon is installed
			if (!addOnInstalled)
				Trace.TraceError("TweetCraft add-on was not detected.");

			// Check whether a supported version of TweetCraft addon is installed
			Version tweetCraftAddonVersion = !addOnInstalled ? new Version(0, 0, 0, 0) : worldOfWarcraft.GetAddOnVersion(TweetCraftAddOnName);

			// Check whether newer version of TweetCraft WoW AddOn is available
			bool updateResult = PerformAddOnUpdateCheck(tweetCraftAddonVersion, worldOfWarcraft);
            if (!updateResult && !addOnInstalled)
            {
                ShowMessageBox(Strings.CouldNotDownoadTweetCraftAddOn, Strings.TweetCraftTitle,
                               ExceptionMessageBoxButtons.OK, ExceptionMessageBoxSymbol.Error, true);

                return false;
            }

		    //Return true if AddOn has already installed but update failed
			return true;
		}

		/// <summary>
		/// Performs the application update check.
		/// </summary>
		/// <returns></returns>
		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		private static void PerformTweetCraftUpdateCheck()
		{
			Trace.TraceInformation("Checking for TweetCraft updates...");

			//Add checking to the ThreadPool
			ThreadPool.QueueUserWorkItem(state =>
			{
				try
				{
					//Create the UpdateManager instance
					var updateManager = new UpdateManager(new DownloadManager(), new Uri(Settings.Default.ApplicationUpdateUrl));
					updateManager.CheckForApplicationUpdates(checkUpdateResult =>
					{
						if (checkUpdateResult.UpdateAvailable)
						{
							//Newer version of application is available
							DialogResult dialogResult = ShowMessageBox(Strings.ApplicationNewerVersionDetected,
																 Strings.TweetCraftTitle,
																 ExceptionMessageBoxButtons.YesNo,
																 ExceptionMessageBoxSymbol.Question, true);

							//Notify the waiting thread
							UpdateManager.Continue();

							if (dialogResult == DialogResult.Yes)
							{
								// Launch application update site url in the default browser
								Process.Start(Settings.Default.ApplicationUpdateLaunchUrl);
							}
						}
						else
						{
							//Notify the waiting thread
							UpdateManager.Continue();
						}
					});
				}
				catch (Exception ex)
				{
					Trace.TraceError(String.Format(CultureInfo.CurrentCulture, "Error checking latest version: {0}", ex.Message));
					UpdateManager.Continue();
				}
			}, null);

			//Wait for checking the application updates on the server
			UpdateManager.Block();
		}

		/// <summary>
		/// Performs the WoW AddOn update check.
		/// </summary>
		/// <param name="tweetCraftAddonVersion">The tweet craft addon version.</param>
		/// <param name="worldOfWarcraft">The world of warcraft.</param>
		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		private bool PerformAddOnUpdateCheck(Version tweetCraftAddonVersion, IWorldOfWarcraft worldOfWarcraft)
		{
            Trace.TraceInformation("Checking for TweetCraft add-on updates...");

			bool result = false;

            //Add checking to the ThreadPool
			ThreadPool.QueueUserWorkItem(state =>
			{
				try
				{
					//Create the UpdateManager instance
					var updateManager = new UpdateManager(new DownloadManager(), new Uri(Settings.Default.AddOnUpdateUrl));
					updateManager.CheckForAddOnUpdates(tweetCraftAddonVersion, checkUpdateResult =>
					{
						if (checkUpdateResult.UpdateAvailable)
						{
							//Newer version of AddOn is available
							ShowNotifyBalloonTip(Strings.TweetCraftTitle, Strings.AddOnNewerVersionDetected, ToolTipIcon.Info);

							try
							{
								//Download the update from the server
								updateManager.GetUpdate(new Uri(TweetCraftAddOnUpdateFileName, UriKind.Relative), content =>
								{
									if (content != null)
									{
										try
										{
											string addOnPath = worldOfWarcraft.GetAddOnPath(TweetCraftAddOnName);

											//Extract AddOn from compressed update file and install it
											var updatePath = updateManager.ExtractAndUpdateFiles(content, addOnPath);
											if (!updatePath)
												throw new InvalidOperationException(Strings.CouldNotInstallAddOn);

											result = true;
										}
										catch (Exception ex)
										{
											Trace.TraceError(String.Format(CultureInfo.CurrentCulture, "Error updating latest version of AddOn: {0}", ex.Message));
											ShowMessageBox(Strings.CouldNotInstallAddOn,
															   Strings.TweetCraftTitle,
															   ExceptionMessageBoxButtons.OK,
															   ExceptionMessageBoxSymbol.Error, true);
										}
										finally
										{
											//Notify the waiting thread
											UpdateManager.Continue();
										}
									}
								});
							}
							catch (Exception ex)
							{
								Trace.TraceError(String.Format(CultureInfo.CurrentCulture, "Error installing latest version of AddOn: {0}", ex.Message));
								ShowMessageBox(Strings.CouldNotInstallAddOn,
											   Strings.TweetCraftTitle,
											   ExceptionMessageBoxButtons.OK,
											   ExceptionMessageBoxSymbol.Error, true);

								//Notify the waiting thread
								UpdateManager.Continue();
							}
						}
						else
						{
							result = true;
							if (!checkUpdateResult.Success)
							{
								result = false;
							}

							//Notify the waiting thread
							UpdateManager.Continue();
						}
					});
				}
				catch (Exception ex)
				{
					Trace.TraceError(String.Format(CultureInfo.CurrentCulture, "Error checking latest version: {0}", ex.Message));
					UpdateManager.Continue();
				}
			}, null);

			//Wait for checking the AddOn updates on the server
			UpdateManager.Block();
			return result;
		}


        private static void InitializeLogging()
        {
            // Only honor LoggingEnabled setting, if not in debug mode
#if !DEBUG
            // Check whether logging is enabled, if not, clear trace listeners
            if (!Settings.Default.LoggingEnabled)
            {
                Trace.Listeners.Clear();
            }
#endif
        }

        private void InitializeErrorHandling()
        {
            // Hook global error events and handle using ExceptionMessageBox
            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;

            // Enable WinForms VisualStyles to make the ExceptionMessageBox truly shine
            System.Windows.Forms.Application.EnableVisualStyles();
        }

        private void InitializeTweetCraft()
        {
            // Initialize TweetCraft
#if DEBUG && !INTEGRATION
            tweetCraft = new TweetCraft(new Twitter.TraceTwitterClient());
#else
            tweetCraft = new TweetCraft();
#endif

            // Apply the settings to TweetCraft
            tweetCraft.ApplySettings();

            // Subscribe to setting changes and apply them to TweetCraft
            Settings.Default.PropertyChanged += (sender, e) => tweetCraft.ApplySettingsProperty(e.PropertyName);

            // Subscribe to tweet and error notifications by TweetCraft
            tweetCraft.Tweeted += OnTweeted;
            tweetCraft.Error += OnError;

            // Update Twitter status and subscribe to status changes
            this.UpdateTwitterStatus();
            tweetCraft.TwitterStatusChanged += OnTwitterStatusChanged;

            // If TweetCraft is incomplete, try to upgrade settings from previous version
            if (!tweetCraft.IsComplete)
                Settings.Default.Upgrade();

            // Check whether the settings are complete, configure TweetCraft if not
            if (!tweetCraft.IsComplete)
            {
                Trace.TraceInformation("TweetCraft settings are incomplete...");
                ShowTweetCraftSettings(tweetCraft);
            }
            else
            {
                // Run TweetCraft
                RunTweetCraft();
            }
        }

        private void InitializeNotifyIcon()
        {
            // Initialize the checked state of the menu items
            ShowNotificationsMenuItem.Checked = ShowNotifications;
            RunOnStartupMenuItem.Checked = IsRunOnStartupEnabled();

            // Add Bold style to Settings item as it is the default behavior for double-click
            SettingsMenuItem.Font = new Font(SettingsMenuItem.Font, FontStyle.Bold);

            // Subscribe to click event
            NotifyIcon.BalloonTipClicked += OnBalloonTipClicked;

            // Make the notify icon visible
            NotifyIcon.Visible = true;
        }

        private void UpdateTwitterStatus()
        {
            if (Thread.CurrentThread == Dispatcher.Thread)
            {
                switch (tweetCraft.TwitterStatus)
                {
                    case TwitterClientStatus.Unknown:
                        TwitterStatusMenuItem.Text = Strings.TwitterStatusUnknownText;
                        break;
                    case TwitterClientStatus.Connected:
                        TwitterStatusMenuItem.Text = Strings.TwitterStatusConnectedText;
                        break;
                    case TwitterClientStatus.NotConnected:
                        TwitterStatusMenuItem.Text = Strings.TwitterStatusNotConnectedText;
                        break;
                    case TwitterClientStatus.NotConnectedRateLimitExceeded:
                        TwitterStatusMenuItem.Text = Strings.TwitterStatusRateLimitExceededText;
                        break;
                }
            }
            else
            {
                Dispatcher.BeginInvoke(new Action(UpdateTwitterStatus));
            }
        }

        private void RunTweetCraft()
        {
            if (tweetCraft.IsComplete)
            {
                // Check whether we should show the startup notification
                if (showStartupNotification)
                {
                    // Show notify balloon tip
                    ShowNotifyBalloonTip(Strings.TweetCraftTitle, Strings.TweetCraftRunningText, ToolTipIcon.Info);
                    showStartupNotification = false;
                }

                // Run TweetCraft
                tweetCraft.Run();
            }
            else
            {
                // Show notify balloon tip
                ShowNotifyBalloonTip(Strings.TweetCraftTitle, Strings.TweetCraftSettingsIncomplete, ToolTipIcon.Warning, ShowTweetCraftSettingsCommand);
            }
        }

        private void ShowTweetCraftSettings(ITweetCraft tweetCraft)
        {
            // Check whether TweetCraft is running, stop it if needed
            if (tweetCraft.IsRunning)
            {
                tweetCraft.Shutdown();
            }

            if (settingsWindow == null)
            {
                // Initialize SettingsWindow
                settingsWindow = new SettingsWindow(tweetCraft);

                // Hook the SettingsWindow and run TweetCraft when the SettingsWindow is hidden
                settingsWindow.IsVisibleChanged += OnSettingsWindowVisibilityChanged;
            }

            Trace.TraceInformation("Activating TweetCraft settings window...");

            if (!settingsWindow.IsVisible)
            {
                settingsWindow.Show();
            }
            else
            {
                settingsWindow.Activate();
            }
        }

        private void OnError(object sender, ErrorEventArgs e)
        {
            if (ShowNotifications)
            {
                // NOTE: Errors are always shown, ShowNotifications is not tested here
#if DEBUG
                // Show the detailed message that includes inner exceptions in debug mode
                string message = e.Exception.GetDetailedMessage();
#else
    // Show error notification with the exception's message
            string message = e.Exception.Message;
#endif

                // Show the balloon tip
                ShowNotifyBalloonTip(Strings.ErrorBalloonTipTitle,
                                     String.Format(CultureInfo.CurrentCulture, Strings.ErrorBalloonTipTextFormat,
                                                   message), ToolTipIcon.Error);
            }
        }

        private void OnTwitterStatusChanged(object sender, EventArgs e)
        {
            this.UpdateTwitterStatus();
        }

        private void OnExitClicked(object sender, EventArgs e)
        {
            if (tweetCraft.IsRunning)
                tweetCraft.Shutdown();

            Shutdown();
        }

        private void OnSettingsClicked(object sender, EventArgs e)
        {
            ShowTweetCraftSettings(tweetCraft);
        }

        private void OnOpenTwitterClicked(object sender, EventArgs e)
        {
            // Launch Twitter's url in the default browser
            Process.Start(TwitterUrl);
        }

        private void OnLaunchWowClicked(object sender, EventArgs e)
        {
            // Initialize a new instance of the World of Warcraft class
            var worldOfWarcraft = new WorldOfWarcraft();

            // Launch World of Warcraft, if it's not running yet
            if (!worldOfWarcraft.IsRunning)
            {
                worldOfWarcraft.Start();
            }
        }

        void OnSettingsWindowVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            // If the SettingsWindow was hidden and TweetCraft is not running, run it
            if (settingsWindow.Visibility == Visibility.Hidden && !tweetCraft.IsRunning)
            {
                Trace.TraceInformation("TweetCraft settings window dismissed.");

                // Run TweetCraft
                RunTweetCraft();
            }
        }

        private void OnShowNotificationsChanged(object sender, EventArgs e)
        {
            ShowNotifications = ShowNotificationsMenuItem.Checked;
        }

        private void OnStartWithWindowsChanged(object sender, EventArgs e)
        {
            UpdateRunOnStartupRegistry(RunOnStartupMenuItem.Checked);
        }

        private void OnTweeted(object sender, TweetEventArgs e)
        {
            if (ShowNotifications)
            {
                string title = String.Empty, text = String.Empty;

                switch (e.Source)
                {
                    case TweetSource.AutoTweetScreenshot:
                        // Show notification bubble with AutoTweet information
                        title = Strings.AutoTweetBalloonTipTitle;
                        text = String.Format(CultureInfo.CurrentCulture, Strings.AutoTweetBalloonTipTextFormat, DateTime.Now.ToShortTimeString());
                        break;
                    case TweetSource.TweetCraft:
                        // Show notification bubble with tweet information
                        title = Strings.TweetBallonTipTitle;
                        text = e.Url != null ?
                                                 String.Format(CultureInfo.CurrentCulture, Strings.TweetBalloonTipTextFormat, e.Tweet.Message, DateTime.Now.ToShortTimeString()) :
                                                 String.Format(CultureInfo.CurrentCulture, Strings.TweetBalloonTipTextNoUrlFormat, e.Tweet.Message, DateTime.Now.ToShortTimeString());
                        break;
                }

                ShowNotifyBalloonTip(title, text, ToolTipIcon.Info, e);
            }
        }

        private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            ExceptionMessageBox exceptionMessageBox;

            // Safely cast the ExceptionObject to an exception
            var exception = e.ExceptionObject as Exception;

            // Check whether an Exception was thrown (most likely ;)
            if (exception != null)
            {
                Trace.TraceError("An unhandled exception occurred. Message: " + exception.Message);

                // Initialize a new instance of the ExceptionMessageBox class using the exception and initialize its properties
                exceptionMessageBox = new ExceptionMessageBox(exception)
                                      {
                                          Symbol = ExceptionMessageBoxSymbol.Error,
                                          Caption = Strings.TweetCraftTooltip
                                      };
            }
            else
            {
                Trace.TraceError("An Unhandled exception occurred.");

                // Initialize a new instance of the ExceptionMessageBox class
                exceptionMessageBox = new ExceptionMessageBox
                                      {
                                          Symbol = ExceptionMessageBoxSymbol.Error,
                                          Caption = Strings.TweetCraftTooltip,
                                          Text = Strings.UnknownErrorText
                                      };
            }

            // Show the exception MessageBox before the application terminates
            exceptionMessageBox.Show(null);

            // Hide the icon in the notification area
            NotifyIcon.Visible = false;
        }

        private void OnBalloonTipClicked(object sender, EventArgs e)
        {
            // Check whether the tag is a TweetEventArgs
            var tweetEventArgs = NotifyIcon.Tag as TweetEventArgs;
            if (tweetEventArgs != null && tweetEventArgs.Url != null)
            {
                // Check the url against the white list of allowed urls
                if (UrlWhitelist.Contains(tweetEventArgs.Url.Host))
                {
                    // Launch the url in the default browser
                    Process.Start(tweetEventArgs.Url.AbsoluteUri);
                }
                return;
            }

            // Check whether the tag is a string command
            var command = NotifyIcon.Tag as string;
            if (command != null && command == ShowTweetCraftSettingsCommand)
            {
                ShowTweetCraftSettings(tweetCraft);
            }
        }

        private ToolStripMenuItem TwitterStatusMenuItem
        {
            get { return (ToolStripMenuItem) NotifyIcon.ContextMenuStrip.Items["twitterStatusMenuItem"]; }
        }

        private ToolStripMenuItem ShowNotificationsMenuItem
        {
            get { return (ToolStripMenuItem)NotifyIcon.ContextMenuStrip.Items["showNotificationsMenuItem"]; }
        }

        private ToolStripMenuItem RunOnStartupMenuItem
        {
            get { return (ToolStripMenuItem)NotifyIcon.ContextMenuStrip.Items["runOnStartupMenuItem"]; }
        }


        private ToolStripMenuItem SettingsMenuItem
        {
            get { return (ToolStripMenuItem)NotifyIcon.ContextMenuStrip.Items["settingsMenuItem"]; }
        }

        private NotifyIcon NotifyIcon
        {
            get { return (NotifyIcon)Resources["TweetCraftNotifyIcon"]; }
        }

        private void ShowNotifyBalloonTip(string title, string text, ToolTipIcon icon, object tag)
        {
            // Set NotifyIcon properties
            NotifyIcon.BalloonTipTitle = title;
            NotifyIcon.BalloonTipText = text;
            NotifyIcon.BalloonTipIcon = icon;
            NotifyIcon.Tag = tag;

            // Show balloon tip
            NotifyIcon.ShowBalloonTip(BalloonTipTimeout);
        }

        private void ShowNotifyBalloonTip(string title, string text, ToolTipIcon icon)
        {
            ShowNotifyBalloonTip(title, text, icon, null);
        }

        /// <summary>
        /// Gets or sets a value indicating whether to show notifications.
        /// </summary>
        private static bool ShowNotifications
        {
            get { return Settings.Default.ShowNotifications; }
            set
            {
                if (Settings.Default.ShowNotifications != value)
                {
                    Settings.Default.ShowNotifications = value;
                    Settings.Default.Save();
                }
            }
        }

		/// <summary>
		/// Shows the message box.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="caption">The caption.</param>
		/// <param name="buttons">The buttons.</param>
		/// <param name="symbol">The symbol.</param>
		/// <param name="noParent">if set to <c>true</c> [no parent].</param>
		/// <returns></returns>
		public static DialogResult ShowMessageBox(string text, string caption, ExceptionMessageBoxButtons buttons, ExceptionMessageBoxSymbol symbol, bool noParent)
        {
            var exceptionMessageBox = new ExceptionMessageBox(text, caption, buttons, symbol);

            if (noParent)
				return exceptionMessageBox.Show(null);

			return exceptionMessageBox.Show(new Win32Window(Process.GetCurrentProcess().MainWindowHandle));
        }

		/// <summary>
		/// Shows the message box.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="caption">The caption.</param>
		/// <param name="buttons">The buttons.</param>
		/// <param name="symbol">The symbol.</param>
        public static void ShowMessageBox(string text, string caption, ExceptionMessageBoxButtons buttons, ExceptionMessageBoxSymbol symbol)
        {
            ShowMessageBox(text, caption, buttons, symbol, false);
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);

            // Dispose managed objects holding onto native resources
            this.Dispose();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (NotifyIcon != null)
            {
                NotifyIcon.Dispose();
            }

            if (mainNativeWindow != null)
            {
                mainNativeWindow.Dispose();
                mainNativeWindow = null;
            }

            if (tweetCraft != null)
            {
                tweetCraft.Dispose();
                tweetCraft = null;
            }

            GC.SuppressFinalize(this);
        }

        private sealed class MainNativeWindow : NativeWindow, IDisposable
        {
            private readonly System.Windows.Application application;

// ReSharper disable InconsistentNaming
            private const int WM_CLOSE = 0x0010;
// ReSharper restore InconsistentNaming

            public MainNativeWindow(System.Windows.Application application)
            {
                if (application == null)
                    throw new ArgumentNullException("application");

                this.application = application;
            }

            ~MainNativeWindow()
            {
                this.Dispose();
            }

            protected override void WndProc(ref Message m)
            {
                // If a WM_CLOSE is received, shutdown the application
                if (m.Msg == WM_CLOSE)
                    application.Shutdown();
                else
                    DefWndProc(ref m);
            }

            public void Dispose()
            {
                if (this.Handle != IntPtr.Zero)
                    this.DestroyHandle();

                GC.SuppressFinalize(this);
            }
        }

        private class Win32Window : IWin32Window
        {
            private readonly IntPtr handle;

            public Win32Window(IntPtr handle)
            {
                this.handle = handle;
            }

            public IntPtr Handle
            {
                get { return handle; }
            }
        }
    }
}